 /*++

Copyright (c) Microsoft Corporation. 
 
This source code is subject to terms and conditions of the Microsoft
Public License. A copy of the license can be found in the License.txt
file at the root of this distribution. By using this source code in 
any fashion, you are agreeing to be bound by the terms of the Microsoft
Public License.
 
You must not remove this notice, or any other, from this software.

Module Name:

    WpdObjectProperties.cpp

Abstract:

    This module implements the WpdObjectProperties helper class
    used to store/retrieve Sensor properties/data
--*/

//
// Dev: Donn Morse
// Revision Date: 03_21_2009
// Reinserted code to add timestamp within GetDataValuesForObject--required for events
//

#include "internal.h"
#include "WpdObjectProperties.h"
#include "WpdObjectProperties.tmh"
#include "RS232Target.h" //Required to access RS232Target object
#include "RS232Connection.h" //Required to access SENSOR_READING_MIN
#include "SensorBaseDriver.h" //Required to access SensorBaseDriver object
#include <strsafe.h> //required for StringCchPrintfA method

const PROPERTYKEY g_SupportedCommonProperties[] =
{
    WPD_OBJECT_ID,
};

const PROPERTYKEY g_SupportedDeviceProperties[] =
{
    WPD_DEVICE_FIRMWARE_VERSION,
    WPD_DEVICE_POWER_LEVEL,
    WPD_DEVICE_POWER_SOURCE,
    WPD_DEVICE_PROTOCOL,
    WPD_DEVICE_MODEL,
    WPD_DEVICE_SERIAL_NUMBER,
    WPD_DEVICE_SUPPORTS_NON_CONSUMABLE,
    WPD_DEVICE_MANUFACTURER,
    WPD_DEVICE_FRIENDLY_NAME,
    WPD_DEVICE_TYPE,
    WPD_FUNCTIONAL_OBJECT_CATEGORY,
};

WpdObjectProperties::WpdObjectProperties() :
    m_SamplingRate(2000)
{
	m_pAccelerometer2D		= new Accelerometer2D(this);
	m_pAccelerometer3D		= new Accelerometer3D(this);
	m_pTemperatureSensor	= new TemperatureSensor(this);
	m_pProximitySensor		= new ProximitySensor(this);
	m_pPresenceSensor		= new PresenceSensor(this);
	m_pCompassSensor		= new CompassSensor(this);
}

WpdObjectProperties::~WpdObjectProperties()
{
    m_pBaseDriver	= NULL;
    m_pTarget		= NULL;

	//TODO: delete references
	m_pAccelerometer2D		= NULL;
	m_pAccelerometer3D		= NULL;
	m_pTemperatureSensor	= NULL;
	m_pProximitySensor		= NULL;
	m_pCompassSensor		= NULL;
}

HRESULT WpdObjectProperties::Initialize(SensorBaseDriver* pBaseDriver)
{
    m_pBaseDriver = pBaseDriver;
    m_pTarget = pBaseDriver->m_pTarget;

    return S_OK;
}

/**
 *  This method is called to populate supported PROPERTYKEYs found on objects.
 *
 *  The parameters sent to us are:
 *  wszObjectID - the object whose supported property keys are being requested
 *  pKeys - An IPortableDeviceKeyCollection to be populated with supported PROPERTYKEYs
 *
 *  The driver should:
 *  Add PROPERTYKEYs pertaining to the specified object.
 */
HRESULT WpdObjectProperties::AddSupportedPropertyKeys(
    LPCWSTR                        wszObjectID,
    IPortableDeviceKeyCollection*  pKeys)
{
    HRESULT     hr          = S_OK;
    CAtlStringW strObjectID = wszObjectID;

    // Add Common PROPERTYKEYs for ALL WPD objects
    AddCommonPropertyKeys(pKeys);

    if (strObjectID.CompareNoCase(WPD_DEVICE_OBJECT_ID) == 0)
    {
        // Add the PROPERTYKEYs for the 'DEVICE' object
        AddDevicePropertyKeys(pKeys);
    }
	else if (strObjectID.CompareNoCase(SENSOR_TEMPERATURE_ID) == 0)
    {
		m_pTemperatureSensor->AddPropertyKeys(pKeys);
    }
    else if (strObjectID.CompareNoCase(SENSOR_COMPASS_ID) == 0)
    {
        m_pCompassSensor->AddPropertyKeys(pKeys);
    }
    else if (strObjectID.CompareNoCase(SENSOR_DUAL_AXIS_ID) == 0)
    {
		m_pAccelerometer2D->AddPropertyKeys(pKeys);
    }
	else if (strObjectID.CompareNoCase(SENSOR_THREE_AXIS_ID) == 0)
    {
		m_pAccelerometer3D->AddPropertyKeys(pKeys);
    }
    else if (strObjectID.CompareNoCase(SENSOR_PIR_ID) == 0)
    {
        m_pPresenceSensor->AddPropertyKeys(pKeys);
    }
    else if (strObjectID.CompareNoCase(SENSOR_PROXIMITY_ID) == 0)
    {
		m_pProximitySensor->AddPropertyKeys(pKeys);
    }

    return hr;
}

/**
 *  This method is called to populate supported PROPERTYKEYs found on data objects.
 *
 *  The parameters sent to us are:
 *  wszObjectID - the object whose supported property keys are being requested
 *  pKeys - An IPortableDeviceKeyCollection to be populated with supported PROPERTYKEYs
 *
 *  The driver should:
 *  Add PROPERTYKEYs pertaining to the specified object.
 */
HRESULT WpdObjectProperties::AddSupportedDataFieldKeys(
    LPCWSTR                        wszObjectID,
    IPortableDeviceKeyCollection*  pKeys)
{
    HRESULT     hr          = S_OK;
    CAtlStringW strObjectID = wszObjectID;

    // Add the PROPERTYKEYs for the object
    if (strObjectID.CompareNoCase(SENSOR_TEMPERATURE_ID) == 0)
    {
        m_pTemperatureSensor->AddDataFieldKeys(pKeys);
    }
	else if (strObjectID.CompareNoCase(SENSOR_PIR_ID) == 0)
    {
		m_pPresenceSensor->AddDataFieldKeys(pKeys);
    }
	else if (strObjectID.CompareNoCase(SENSOR_COMPASS_ID) == 0)
    {
        m_pCompassSensor->AddDataFieldKeys(pKeys);
    }
	else if (strObjectID.CompareNoCase(SENSOR_DUAL_AXIS_ID) == 0)
    {
        m_pAccelerometer2D->AddDataFieldKeys(pKeys);
    }
	else if (strObjectID.CompareNoCase(SENSOR_THREE_AXIS_ID) == 0)
    {
        m_pAccelerometer3D->AddDataFieldKeys(pKeys);
    }
	else if (strObjectID.CompareNoCase(SENSOR_PROXIMITY_ID) == 0)
    {
        m_pProximitySensor->AddDataFieldKeys(pKeys);
    }

    return hr;
}

/**
 *  This method is called to populate common PROPERTYKEYs found on ALL objects.
 *
 *  The parameters sent to us are:
 *  pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs
 *
 *  The driver should:
 *  Add PROPERTYKEYs pertaining to the ALL objects.
 */
VOID AddCommonPropertyKeys(
    IPortableDeviceKeyCollection* pKeys)
{
    if (pKeys != NULL)
    {
        for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedCommonProperties); dwIndex++)
        {
            pKeys->Add(g_SupportedCommonProperties[dwIndex] );
        }
    }
}

/**
 *  This method is called to populate common PROPERTYKEYs found on the DEVICE object.
 *
 *  The parameters sent to us are:
 *  pKeys - An IPortableDeviceKeyCollection to be populated with PROPERTYKEYs
 *
 *  The driver should:
 *  Add PROPERTYKEYs pertaining to the DEVICE object.
 */
VOID AddDevicePropertyKeys(
    IPortableDeviceKeyCollection* pKeys)
{
    if (pKeys != NULL)
    {
        for (DWORD dwIndex = 0; dwIndex < ARRAYSIZE(g_SupportedDeviceProperties); dwIndex++)
        {
            pKeys->Add(g_SupportedDeviceProperties[dwIndex] );
        }
    }
}

/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose supported properties have
 *      been requested.
 *
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_FILTER: the filter to use when returning supported properties.
 *      Since this parameter is optional, it may not exist.
 *      ! This driver currently ignores the filter parameter. !
 *
 *  The driver should:
 *  - Return supported property keys for the specified object in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS
 */
HRESULT WpdObjectProperties::OnGetSupportedProperties(
        __in LPWSTR  wszObjectID,
        IPortableDeviceKeyCollection** ppKeys )
{
    HRESULT hr          = S_OK;

    // CoCreate a collection to store the supported property keys.
    hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceKeyCollection,
                              (VOID**) ppKeys);

    // Add supported property keys for the specified object to the collection
    if (hr == S_OK)
    {
        hr = this->AddSupportedPropertyKeys(wszObjectID, *ppKeys);
    }

    return hr;
}

/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_SUPPORTED
 *  command for the data child object of the wszObjectID.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose supported properties have
 *      been requested.
 *
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_FILTER: the filter to use when returning supported properties.
 *      Since this parameter is optional, it may not exist.
 *      ! This driver currently ignores the filter parameter. !
 *
 *  The driver should:
 *  - Return supported property keys for the specified object in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS
 */
HRESULT WpdObjectProperties::OnGetSupportedDataFields(
        __in LPWSTR  wszObjectID,
        IPortableDeviceKeyCollection** ppKeys )
{
    HRESULT hr          = S_OK;

    // CoCreate a collection to store the supported property keys.
    hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceKeyCollection,
                              (VOID**) ppKeys);

    // Add supported property keys for the specified object to the collection
    if (hr == S_OK)
    {
        hr = this->AddSupportedDataFieldKeys(wszObjectID, *ppKeys);
    }

    return hr;
}

/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values have been requested.
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys, identifying which
 *      specific property values we are requested to return.
 *
 *  The driver should:
 *  - Return all requested property values in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES.  If any property read failed, the corresponding value should be
 *      set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure.
 *  - S_OK should be returned if all properties were read successfully.
 *  - S_FALSE should be returned if any property read failed.
 *  - Any error return indicates that the driver did not fill in any results, and the caller will
 *      not attempt to unpack any property values.
 */
HRESULT WpdObjectProperties::OnGetPropertyValues(
        __in LPWSTR  wszObjectID,
        IPortableDeviceKeyCollection* pKeys,
        IPortableDeviceValues** ppValues )
{
    HRESULT hr          = S_OK;

    // CoCreate a collection to store the property values.
    hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) ppValues);

    // Read the specified properties on the specified object and add the property values to the collection.
    if (hr == S_OK)
    {
        hr = this->GetPropertyValuesForObject(wszObjectID, pKeys, *ppValues);
    }

    return hr;
}

/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET
 *  command for the data child object of wszObjectID.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values have been requested.
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys, identifying which
 *      specific property values we are requested to return.
 *
 *  The driver should:
 *  - Return all requested property values in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES.  If any property read failed, the corresponding value should be
 *      set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure.
 *  - S_OK should be returned if all properties were read successfully.
 *  - S_FALSE should be returned if any property read failed.
 *  - Any error return indicates that the driver did not fill in any results, and the caller will
 *      not attempt to unpack any property values.
 */
HRESULT WpdObjectProperties::OnGetDataFields(
        __in LPWSTR  wszObjectID,
        IPortableDeviceKeyCollection* pKeys,
        IPortableDeviceValues** ppValues )
{
    HRESULT hr          = S_OK;

    // CoCreate a collection to store the property values.
    hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) ppValues);

    // Read the specified properties on the specified object and add the property values to the collection.
    if (hr == S_OK)
    {
        hr = this->GetDataValuesForObject(wszObjectID, pKeys, *ppValues);
    }

    return hr;
}

/*
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_ALL
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values have been requested.
 *
 *  The driver should:
 *  - Return all property values in WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES.  If any property read failed, the corresponding value should be
 *      set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure.
 *  - S_OK should be returned if all properties were read successfully.
 *  - S_FALSE should be returned if any property read failed.
 *  - Any error return indicates that the driver did not fill in any results, and the caller will
 *      not attempt to unpack any property values.
 */
HRESULT WpdObjectProperties::OnGetAllPropertyValues(
    IPortableDeviceValues*  pParams,
    IPortableDeviceValues*  pResults)
{
    HRESULT hr          = S_OK;
    LPWSTR  wszObjectID = NULL;
    CComPtr<IPortableDeviceValues>        pValues;
    CComPtr<IPortableDeviceKeyCollection> pKeys;

     //First get ALL parameters for this command.  If we cannot get ALL parameters
     //then E_INVALIDARG should be returned and no further processing should occur.

    /* Get the object identifier whose property values have been requested*/
    if (hr == S_OK)
    {
        hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID);
    }

    /* CoCreate a collection to store the property values.*/
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) &pValues);
    }

     //CoCreate a collection to store the property keys we are going to use
     //to request the property values of.
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceKeyCollection,
                              (VOID**) &pKeys);
    }

     //First we make a request for ALL supported property keys for the specified object.
     //Next, we delegate to our helper function GetPropertyValuesForObject( ) passing
     //the entire property key collection.  This will reuse existing implementation
     //in our driver to perform the GetAllPropertyValues operation.
    if (hr == S_OK)
    {
        hr = AddSupportedPropertyKeys(wszObjectID, pKeys);
        if (hr == S_OK)
        {
            hr = GetPropertyValuesForObject(wszObjectID, pKeys, pValues);
        }
    }

     //S_OK or S_FALSE can be returned from GetPropertyValuesForObject( ).
     //S_FALSE means that 1 or more property values could not be retrieved successfully.
     //The value for the specified property key should be set to the error HRESULT of
     //the reason why the property could not be read.
     //(i.e. an error of HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED) if a property value was
     // requested and is not supported by the specified object.)
    if (SUCCEEDED(hr))
    {
        // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES value in the results
        HRESULT hrTemp = S_OK;
        hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES, pValues);

        if(FAILED(hrTemp))
        {
            hr = hrTemp;
        }
    }

    // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
    CoTaskMemFree(wszObjectID);

    return hr;
}

/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_SET
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property values we want to return.
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_VALUES: an IPortableDeviceValues of values, identifying which
 *      specific property values we are requested to write.
 *
 *  The driver should:
 *  - Write all requested property values.  For each property, a write result should be returned in the
 *      write result property store.
 *  - If any property write failed, the corresponding write result value should be
 *      set to type VT_ERROR with the 'scode' member holding the HRESULT reason for the failure.
 *  - S_OK should be returned if all properties were written successfully.
 *  - S_FALSE should be returned if any property write failed.
 *  - Any error return indicates that the driver did not write any results, and the caller will
 *      not attempt to unpack any property write results.
 */
HRESULT WpdObjectProperties::OnSetPropertyValues(
        __in LPWSTR  wszObjectID,
        IPortableDeviceValues* pValues,
        IPortableDeviceValues** ppResults
        )
{
    HRESULT hr          = S_OK;
    CAtlStringW strObjectID = wszObjectID;
    DWORD   cValues     = 0;
    BOOL fHasError = FALSE;

    // CoCreate a collection to store the property set operation results.
    hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) ppResults);

    // Set the property values on the specified object
    if (hr == S_OK)
    {
        hr = pValues->GetCount(&cValues);

        if (hr == S_OK)
        {
            for (DWORD dwIndex = 0; dwIndex < cValues; dwIndex++)
            {
                PROPERTYKEY Key = WPD_PROPERTY_NULL;
                PROPVARIANT var;

                PropVariantInit( &var );

                hr = pValues->GetAt(dwIndex, &Key, &var);

                if (hr == S_OK)
                {
                    //Check for settable properties

                    //if( IsEqualPropertyKey( Key, SENSOR_PROPERTY_SAMPLING_RATE ) )
                    if( IsEqualPropertyKey( Key, SENSOR_PROPERTY_CURRENT_REPORT_INTERVAL ) )
                    {
                        if( var.vt == VT_UI4 )
                        {
                            if ((strObjectID.CompareNoCase(SENSOR_PIR_ID) == 0)||
                                (strObjectID.CompareNoCase(SENSOR_PROXIMITY_ID) == 0) ||
                                (strObjectID.CompareNoCase(SENSOR_COMPASS_ID) == 0) ||
                                (strObjectID.CompareNoCase(SENSOR_DUAL_AXIS_ID) == 0) ||
                                (strObjectID.CompareNoCase(SENSOR_THREE_AXIS_ID) == 0) ||
                                (strObjectID.CompareNoCase(SENSOR_TEMPERATURE_ID) == 0))
                            {
                                SendUpdateIntervalToDevice((DWORD)var.ulVal);
                                m_SamplingRate = var.ulVal;
                            }
                            else
                            {
                                hr = (*ppResults)->SetErrorValue(Key, E_INVALIDARG);
                                fHasError = TRUE;
                            }
                        }
                        else if( var.vt == VT_EMPTY )
                        {//Do not allow delete the property
                            hr = (*ppResults)->SetErrorValue(Key, E_ACCESSDENIED);
                            fHasError = TRUE;
                        }
                        else
                        {
                            hr = (*ppResults)->SetErrorValue(Key, E_INVALIDARG);
                            fHasError = TRUE;
                        }
                    } // end if( IsEqualPropertyKey...
                    else
                    {
                        hr = (*ppResults)->SetErrorValue(Key, E_ACCESSDENIED);
                        fHasError = TRUE;
                    }
                } // end if (hr == S_OK)

                if( FAILED( hr ) )
                {
                    break;
                }
            } // end for (DWORD dwIndex = 0;
        } // end if (hr == S_OK) after GetCount...

        // Since we have set failures for the property set operations we must let the application
        // know by returning S_FALSE. This will instruct the application to look at the
        // property set operation results for failure values.
        if( (hr == S_OK) && fHasError )
        {
            hr = S_FALSE;
        }
    } // end if (hr == S_OK) after CoCreateInstance

    return hr;
}

/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_GET_ATTRIBUTES
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose property attributes we want to return.
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys containing a single value,
 *      which is the key identifying the specific property attributes we are requested to return.
 *
 *  The driver should:
 *  - Return the requested property attributes.  If any property attributes failed to be retrieved,
 *    the corresponding value should be set to type VT_ERROR with the 'scode' member holding the
 *    HRESULT reason for the failure.
 *  - S_OK should be returned if all property attributes were read successfully.
 *  - S_FALSE should be returned if any property attribute failed.
 *  - Any error return indicates that the driver did not fill in any results, and the caller will
 *      not attempt to unpack any property values.
 */
HRESULT WpdObjectProperties::OnGetPropertyAttributes(
    IPortableDeviceValues*  pParams,
    IPortableDeviceValues*  pResults)
{
    HRESULT     hr          = S_OK;
    LPWSTR      wszObjectID = NULL;
    PROPERTYKEY Key         = WPD_PROPERTY_NULL;
    CComPtr<IPortableDeviceValues>  pAttributes;

    // First get ALL parameters for this command.  If we cannot get ALL parameters
    // then E_INVALIDARG should be returned and no further processing should occur.

    // Get the object identifier whose property attributes have been requested
    if (hr == S_OK)
    {
        hr = pParams->GetStringValue(WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID, &wszObjectID);
    }

    // Get the list of property keys whose attributes are being requested
    if (hr == S_OK)
    {
        hr = pParams->GetKeyValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS, &Key);
    }

    // CoCreate a collection to store the property attributes.
    if (hr == S_OK)
    {
        hr = CoCreateInstance(CLSID_PortableDeviceValues,
                              NULL,
                              CLSCTX_INPROC_SERVER,
                              IID_IPortableDeviceValues,
                              (VOID**) &pAttributes);
    }

    // Get the attributes for the specified properties on the specified object and add them
    // to the collection.
    if (hr == S_OK)
    {
        hr = GetPropertyAttributesForObject(wszObjectID, Key, pAttributes);
    }

    if (SUCCEEDED(hr))
    {
        // Set the WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_ATTRIBUTES value in the results
        HRESULT hrTemp = S_OK;
        hrTemp = pResults->SetIPortableDeviceValuesValue(WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_ATTRIBUTES, pAttributes);

        if(FAILED(hrTemp))
        {
            hr = hrTemp;
        }
    }

    // Free the memory.  CoTaskMemFree ignores NULLs so no need to check.
    CoTaskMemFree(wszObjectID);

    return hr;
}

/**
 *  This method is called when we receive a WPD_COMMAND_OBJECT_PROPERTIES_DELETE
 *  command.
 *
 *  The parameters sent to us are:
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_OBJECT_ID: identifies the object whose properties should be deleted.
 *  - WPD_PROPERTY_OBJECT_PROPERTIES_PROPERTY_KEYS: a collection of property keys indicating which
 *      properties to delete.
 *
 *  The driver should:
 *  - Delete the specified properties from the object.
 *  - S_OK should be returned if all specified properties were successfully deleted.
 *  - E_ACCESSDENIED should be returned if the client attempts to delete a property which is not deletable (i.e.
 *    WPD_PROPERTY_ATTRIBUTE_CAN_DELETE is FALSE for that property.)
 */
HRESULT WpdObjectProperties::OnDeleteProperties(
    IPortableDeviceValues*  pParams,
    IPortableDeviceValues*  pResults)
{
    HRESULT hr = E_ACCESSDENIED;

    UNREFERENCED_PARAMETER(pParams);
    UNREFERENCED_PARAMETER(pResults);

    // This driver has no properties which can be deleted.

    return hr;
}

/**
 *  This method is called to populate property values for the object specified.
 *
 *  The parameters sent to us are:
 *  wszObjectID - the object whose properties are being requested.
 *  pKeys - the list of property keys of the properties to request from the object
 *  pValues - an IPortableDeviceValues which will contain the property values retreived from the object
 *
 *  The driver should:
 *  Read the specified properties for the specified object and populate pValues with the
 *  results.
 */
HRESULT WpdObjectProperties::GetPropertyValuesForObject(
    LPCWSTR                        wszObjectID,
    IPortableDeviceKeyCollection*  pKeys,
    IPortableDeviceValues*         pValues)
{
    HRESULT     hr          = S_OK;
    CAtlStringW strObjectID = wszObjectID;
    DWORD       cKeys       = 0;

    if ((wszObjectID == NULL) ||
        (pKeys       == NULL) ||
        (pValues     == NULL))
    {
        hr = E_INVALIDARG;
        return hr;
    }

    hr = pKeys->GetCount(&cKeys);

    if (hr == S_OK)
    {
        // Get values for the DEVICE object
        if (strObjectID.CompareNoCase(WPD_DEVICE_OBJECT_ID) == 0)
        {
            for (DWORD dwIndex = 0; dwIndex < cKeys; dwIndex++)
            {
                PROPERTYKEY Key = WPD_PROPERTY_NULL;
                hr = pKeys->GetAt(dwIndex, &Key);

                if (hr == S_OK)
                {
                    // Preset the property value to 'error not supported'.  The actual value
                    // will replace this value, if read from the device.
                    pValues->SetErrorValue(Key, HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED));

                    // Set DEVICE object properties
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_FIRMWARE_VERSION))
                    {
                        hr = pValues->SetStringValue(WPD_DEVICE_FIRMWARE_VERSION, DEVICE_FIRMWARE_VERSION_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_POWER_LEVEL))
                    {
                        hr = pValues->SetUnsignedIntegerValue(WPD_DEVICE_POWER_LEVEL, DEVICE_POWER_LEVEL_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_POWER_SOURCE))
                    {
                        hr = pValues->SetUnsignedIntegerValue(WPD_DEVICE_POWER_SOURCE, WPD_POWER_SOURCE_EXTERNAL);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_PROTOCOL))
                    {
                        hr = pValues->SetStringValue(WPD_DEVICE_PROTOCOL, DEVICE_PROTOCOL_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_MODEL))
                    {
                        hr = pValues->SetStringValue(WPD_DEVICE_MODEL, DEVICE_MODEL_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_SERIAL_NUMBER))
                    {
                        hr = pValues->SetStringValue(WPD_DEVICE_SERIAL_NUMBER, DEVICE_SERIAL_NUMBER_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_SUPPORTS_NON_CONSUMABLE))
                    {
                        hr = pValues->SetBoolValue(WPD_DEVICE_SUPPORTS_NON_CONSUMABLE, DEVICE_SUPPORTS_NONCONSUMABLE_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_MANUFACTURER))
                    {
                        hr = pValues->SetStringValue(WPD_DEVICE_MANUFACTURER, DEVICE_MANUFACTURER_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_FRIENDLY_NAME))
                    {
                        hr = pValues->SetStringValue(WPD_DEVICE_FRIENDLY_NAME, DEVICE_FRIENDLY_NAME_VALUE);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_DEVICE_TYPE))
                    {
                        hr = pValues->SetUnsignedIntegerValue(WPD_DEVICE_TYPE, WPD_DEVICE_TYPE_GENERIC);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_OBJECT_ID))
                    {
                        hr = pValues->SetStringValue(WPD_OBJECT_ID, WPD_DEVICE_OBJECT_ID);
                    }
                    else
                    if (IsEqualPropertyKey(Key, WPD_FUNCTIONAL_OBJECT_CATEGORY))
                    {
                        hr = pValues->SetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, WPD_FUNCTIONAL_CATEGORY_DEVICE);
                    }
                }
            }
        }
        else if (strObjectID.CompareNoCase(SENSOR_TEMPERATURE_ID) == 0)
        {
			hr = m_pTemperatureSensor->GetPropertyValues(pKeys, pValues);
        }
        else if (strObjectID.CompareNoCase(SENSOR_PIR_ID) == 0)
        {
			hr = m_pPresenceSensor->GetPropertyValues(pKeys, pValues);
        }
        else if (strObjectID.CompareNoCase(SENSOR_PROXIMITY_ID) == 0)
        {
			hr = m_pProximitySensor->GetPropertyValues(pKeys, pValues);
        }
        else if (strObjectID.CompareNoCase(SENSOR_COMPASS_ID) == 0)
        {
			hr = m_pCompassSensor->GetPropertyValues(pKeys, pValues);
        }
        else if (strObjectID.CompareNoCase(SENSOR_DUAL_AXIS_ID) == 0)
        {
			hr = m_pAccelerometer2D->GetPropertyValues(pKeys, pValues);
        }
		else if (strObjectID.CompareNoCase(SENSOR_THREE_AXIS_ID) == 0)
        {
			hr = m_pAccelerometer3D->GetPropertyValues(pKeys, pValues);
        }
	}
    return hr;
}

HRESULT WpdObjectProperties::UpdateSensorReadingForObject(LPCWSTR wszObjectID, LONGLONG llSensorReading) 
{
	HRESULT hr = S_OK;
    CAtlStringW strObjectID = wszObjectID;

	if (strObjectID.CompareNoCase(SENSOR_DUAL_AXIS_ID) == 0)
    {
		hr = m_pAccelerometer2D->UpdateSensorReading(llSensorReading);
	}
	else if (strObjectID.CompareNoCase(SENSOR_THREE_AXIS_ID) == 0)
    {
		hr = m_pAccelerometer3D->UpdateSensorReading(llSensorReading);
	}
	else if (strObjectID.CompareNoCase(SENSOR_TEMPERATURE_ID) == 0)
    {
		hr = m_pTemperatureSensor->UpdateSensorReading(llSensorReading);
	}
	else if (strObjectID.CompareNoCase(SENSOR_PROXIMITY_ID) == 0)
    {
		hr = m_pProximitySensor->UpdateSensorReading(llSensorReading);
	}
	else if (strObjectID.CompareNoCase(SENSOR_PIR_ID) == 0)
    {
		hr = m_pPresenceSensor->UpdateSensorReading(llSensorReading);
	}
	else if (strObjectID.CompareNoCase(SENSOR_COMPASS_ID) == 0)
    {
		hr = m_pCompassSensor->UpdateSensorReading(llSensorReading);
	}

	return hr;
}

/**
 *  This method is called to populate data values for the object specified.
 *
 *  The parameters sent to us are:
 *  wszObjectID - the object whose properties are being requested.
 *  pKeys - the list of property keys of the properties to request from the object
 *  pValues - an IPortableDeviceValues which will contain the property values retreived from the object
 *
 *  The driver should:
 *  Read the specified properties for the specified object and populate pValues with the
 *  results.
 */
HRESULT WpdObjectProperties::GetDataValuesForObject(
    LPCWSTR                        wszObjectID,
    IPortableDeviceKeyCollection*  pKeys,
    IPortableDeviceValues*         pValues)
{
    HRESULT     hr          = S_OK;
    CAtlStringW strObjectID = wszObjectID;
    DWORD       cKeys       = 0;

    if ((wszObjectID == NULL) ||
        (pKeys       == NULL) ||
        (pValues     == NULL))
    {
        hr = E_INVALIDARG;
        return hr;
    }

    hr = pKeys->GetCount(&cKeys);
    if (hr == S_OK)
    {
        if (strObjectID.CompareNoCase(SENSOR_TEMPERATURE_ID) == 0)
        {
			m_pTemperatureSensor->GetDataValues(pKeys, pValues);
        }
        else if (strObjectID.CompareNoCase(SENSOR_DUAL_AXIS_ID) == 0)
        {
			hr = m_pAccelerometer2D->GetDataValues(pKeys,pValues);
        }
		else if (strObjectID.CompareNoCase(SENSOR_THREE_AXIS_ID) == 0)
        {
			hr = m_pAccelerometer3D->GetDataValues(pKeys,pValues);
        }
        else if (strObjectID.CompareNoCase(SENSOR_PROXIMITY_ID) == 0)
        {
			hr = m_pProximitySensor->GetDataValues(pKeys,pValues);
        }
        else if (strObjectID.CompareNoCase(SENSOR_COMPASS_ID) == 0)
        {
			hr = m_pCompassSensor->GetDataValues(pKeys,pValues);
        }
        else if (strObjectID.CompareNoCase(SENSOR_PIR_ID) == 0)
        {
			hr = m_pPresenceSensor->GetDataValues(pKeys,pValues);
        }
	}
    return hr;
}

/**
 *  This method is called to populate property attributes for the object and property specified.
 *
 *  The parameters sent to us are:
 *  wszObjectID - the object whose property attributes are being requested.
 *  Key - the property whose attributes are being requested
 *  pAttributes - an IPortableDeviceValues which will contain the resulting property attributes
 *
 *  The driver should:
 *  Read the property attributes for the specified property on the specified object and
 *  populate pAttributes with the results.
 */
HRESULT WpdObjectProperties::GetPropertyAttributesForObject(
    LPCWSTR                wszObjectID,
    const PROPERTYKEY&     Key,
    IPortableDeviceValues* pAttributes)
{
    HRESULT hr = S_OK;

    if ((wszObjectID == NULL) ||
        (pAttributes == NULL))
    {
        hr = E_INVALIDARG;
        return hr;
    }

    UNREFERENCED_PARAMETER(wszObjectID);
    UNREFERENCED_PARAMETER(Key);

    //
    // Since ALL of our properties have the same attributes, we are ignoring the
    // passed in wszObjectID and Key parameters.  These parameters allow you to
    // customize attributes for properties on specific objects. (i.e. WPD_OBJECT_ORIGINAL_FILE_NAME
    // may be READ/WRITE on some objects and READONLY on others. )
    //

    if (hr == S_OK)
    {
        hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_DELETE, FALSE);
    }

    if (hr == S_OK)
    {
        hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_READ, TRUE);
    }

    if (hr == S_OK)
    {
        hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_CAN_WRITE, FALSE);
    }

    if (hr == S_OK)
    {
        hr = pAttributes->SetBoolValue(WPD_PROPERTY_ATTRIBUTE_FAST_PROPERTY, TRUE);
    }

    if (hr == S_OK)
    {
        hr = pAttributes->SetUnsignedIntegerValue(WPD_PROPERTY_ATTRIBUTE_FORM, WPD_PROPERTY_ATTRIBUTE_FORM_UNSPECIFIED);
    }

    return hr;
}

/**
 *  This method is called to update the sensor interval on the device
 *
 *  The parameters sent to us are:
 *  dwNewInterval - the sensor update interval to set
 *
 *  The driver should:
 *  Update the cached sensor property and send a write request to the device
 */
HRESULT WpdObjectProperties::SendUpdateIntervalToDevice(DWORD dwNewInterval)
{    
    HRESULT      hr                           = S_OK;
    RS232Target* pDeviceTarget                = NULL;

    CHAR  szInterval[INTERVAL_DATA_LENGTH+1]  = {0};

    // Check the input value
    if (IsValidUpdateInterval(dwNewInterval) == FALSE)
    {
        hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
    }

    // Format a write request with the input value
    if (hr == S_OK)
    {
        hr = StringCchPrintfA(szInterval, ARRAYSIZE(szInterval), "%d", dwNewInterval);
    }

    // Send the write request to the device
    if (hr == S_OK)
    {   
        pDeviceTarget = m_pBaseDriver->GetRS232Target();
 
        if (pDeviceTarget->IsReady())
        {
            hr = pDeviceTarget->SendWriteRequest((BYTE *)szInterval, sizeof(szInterval));    
            if (hr == S_OK)
            {
            }
        }
        else
        {
            hr = HRESULT_FROM_WIN32(ERROR_NOT_READY);
        }
    }
    
    //if (hr == S_OK)
    if ((hr == S_OK) && (m_pTarget != NULL))
    {
        // Update the cached value on the driver
        //SetUpdateInterval(dwNewInterval);
    }

    return hr;
}


/**
 *  This method is called to check that interval value falls within the accepted range
 *  of 2000 to 60000 milliseconds
 *
 *  The parameters sent to us are: 
 *  wszInterval - the sensor update interval to check
 *
 *  The driver should:
 *  Return TRUE if the interval is within range
 */
BOOL WpdObjectProperties::IsValidUpdateInterval(DWORD dwInterval)
{
    if ((dwInterval >= SENSOR_UPDATE_INTERVAL_MIN) && 
        (dwInterval <= SENSOR_UPDATE_INTERVAL_MAX))
    {
        return TRUE;
    }

    return FALSE;
}

/**
 *  This method is called to retrieve the current sampling rate
 *
 *  The parameters sent to us are: 
 *  None.
 *
 *  The driver should:
 *  Return the current sampling rate
 */

ULONG WpdObjectProperties::GetSamplingRate()
{
    // Ensure that this value isn't currently being accessed by another thread
    CComCritSecLock<CComAutoCriticalSection> Lock(m_SamplingRateCriticalSection);
    return (m_SamplingRate);
}

/**
 *  This method is called to set the current sampling rate
 *
 *  The parameters sent to us are: 
 *  ulRate - the new requested rate
 *
 *  The driver should:
 *  Return nothing
 */

VOID WpdObjectProperties::SetSamplingRate(ULONG ulRate)
{
    // Ensure that this value isn't currently being accessed by another thread
    CComCritSecLock<CComAutoCriticalSection> Lock(m_SamplingRateCriticalSection);
    m_SamplingRate = ulRate;
}



